%{ 
	open Ast ;;
  open LatticeCreation

%}

%token LPAREN RPAREN LBRACE RBRACE LSQBRACKET RSQBRACKET EOF
%token PLUS MINUS TIMES DIVIDE PERCENT PLUS_PLUS MINUS_MINUS
%token PIPE TILDE QUEST SEMI COLON DOT AT CARET
%token ASSIGN QUEST_ASSIGN DEF
%token AND OR EQ NEQ EXCLAMATION LT GT LEQ GEQ
%token EPSILON NIL TRUE FALSE
%token  LET THIS NO_TOKEN FUNC_CALL
/*LENGTH  COUNT  LABELS  CLONE  REVERSE  RETURN  LOOP  GET_LOST*/
%token <int> PREDEFINED_LABEL
%token <string> STRING_LITERAL
%token <string> ID
%token <float> FLOAT
%token <int> INTEGER

%left NO_TOKEN
%left COLON
%left LET
/* Assignment Operators: */
%right ASSIGN QUEST_ASSIGN
/* Lattice Operators: */ 
%left SEMI
%left PIPE	
%left TILDE
/* Logical Operators: */ 
%left OR 
%left AND 
/* Comparison Operators: */ 
%left EQ NEQ 
%left LT LEQ GT GEQ 
/* Arithmatic Operators: */ 
%left PLUS MINUS
%left TIMES DIVIDE PERCENT
/* Unary Operators Operators: */ 
%left UNI_MINUS EXCLAMATION PLUS_PLUS MINUS_MINUS QUEST CARET
/* Name Operators: */ 
%left LBRACE LSQBRACKET LPAREN
%left DOT
%left AT
%left DEF

%start lattice
%type <Ast.lattice> lattice

%%

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*=-=-=-=-=               DEFINING LATTICEs               =-=-=-=-=-=*/
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
lattice:
	  labeled_altlat         	 		    		{ ([(createLatticeFieldsFromLatticeType $1)], env) }
	| labeled_altlat SEMI lattice         { (appendListForSeqlat((createLatticeFieldsFromLatticeType $1), (fst $3)), env) } 
	| error SEMI lattice    	            { addToErrorTable(SynParseError, None); (appendListForSeqlat((createLatticeFieldsFromLatticeType(Expr(newExprFields(ConstExpr(Nil))))), (fst $3)), env) }
	| error                 	            { addToErrorTable(SynParseError, None); ([createLatticeFieldsFromLatticeType(Expr(newExprFields(ConstExpr(Nil))))], env) }
	
labeled_altlat: 
	  altlat       	       	 			   			{ incLabelIdGen env.labelIdGenerator; $1 }
	| label COLON altlat  	 			   			{ incLabelIdGen env.labelIdGenerator; createLabeledAltlat($1, $3) }
	| function_header COLON altlat  { incLabelIdGen env.labelIdGenerator; createParamLabeledAltlat(fst $1, snd $1, $3)  } 

function_header:
	  DEF ID LPAREN param_list RPAREN     { popOldSymbolTable(env); ($2, $4) }

param_list: 
	  ID  			   			   			   			  { [$1] }
	| param_list SEMI ID   			   			  { $3::$1 }				

altlat:
	  /*epsilon*/      			   			    	{ Expr(newExprFields(ConstExpr(Epsilon))) }
	| expr 			   			   			   			  { Expr($1) }
	| LSQBRACKET expr RSQBRACKET expr     { createPredicateLattice($2, $4) }
	| altlat PIPE altlat 	                { appendListForAltlat($1, $3) }

label: 
	  ID { 	$1	}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*=-=-=-=-=-=             Defining Expressions              =-=-=-=-=*/
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/

expr:
	  lvalue  	  	      	  	             { $1 }
	| name QUEST_ASSIGN expr 		  	         { newExprFields(createBinOp($1,Assign,newExprFields(createUnaryOp(Eval, $3)))) }
	| lvalue ASSIGN expr 	    	  	         { newExprFields(createBinOp($1,Assign,$3)) }
	| DEF ID ASSIGN expr                     { createLocal($2, $4) }
	| function_header ASSIGN expr	           { createParamLocal(fst $1, snd $1, $3) }
	| QUEST lvalue  			    	  	         { newExprFields(createUnaryOp(Eval, $2))  }
 		
	| LET expr                	  	         { newExprFields(createUnaryOp(Let, $2))}
	| LPAREN lattice RPAREN DOT rest_of_name { createDirectAccessExprFields($2, $5) }
  | name LPAREN lattice RPAREN             { newExprFields(createBinOp($1, FuncCall, createParenthisizedLattice($3))) }

	| number                  	  	         { $1 }
	| STRING_LITERAL          	  	         { newExprFields(ConstExpr(StringLiteral($1))) }
	| EPSILON           	  	               { newExprFields(ConstExpr(Epsilon)) }
	| NIL                    	  	           { newExprFields(ConstExpr(Nil)) }
	| TRUE             	  	                 { newExprFields(ConstExpr(True)) }
	| FALSE            	  	                 { newExprFields(ConstExpr(False)) }

	| expr PLUS expr   	     	               { newExprFields(createBinOp($1,Plus,$3)) }		
	| expr MINUS expr     		  	      		 { newExprFields(createBinOp($1,Minus,$3)) }
	| expr TIMES expr 		  	      			   { newExprFields(createBinOp($1,Times,$3)) }
	| expr DIVIDE expr  	  	      	   		 { newExprFields(createBinOp($1,Divide,$3)) }
	| expr PERCENT expr 	  	          		 { newExprFields(createBinOp($1,Percent,$3)) }
	  	      
	| expr OR expr    	  	                 { newExprFields(createBinOp($1,Or,$3)) }
	| expr AND expr  	  	                   { newExprFields(createBinOp($1,And,$3)) }

	| expr EQ expr 		  	      			       { newExprFields(createBinOp($1,Equal,$3)) }
	| expr NEQ expr 			  	      	       { newExprFields(createBinOp($1,NotEqual,$3)) }
	| expr LT expr 					  	             { newExprFields(createBinOp($1,Less,$3)) }
	| expr LEQ expr 				  	      	     { newExprFields(createBinOp($1,LessOrEqual,$3)) }
	| expr GT expr 				    	  	         { newExprFields(createBinOp($1,Greater,$3)) }
	| expr GEQ expr 			 			  	         { newExprFields(createBinOp($1,GreaterOrEqual,$3)) }

	| expr TILDE expr                        { newExprFields(createBinOp($1,Tilde,$3)) }
 
	| MINUS expr %prec UNI_MINUS             { newExprFields(createUnaryOp(UniMinus, $2)) }

	| EXCLAMATION expr 			   			         { newExprFields(createUnaryOp(Not, $2)) }

	| CARET LPAREN lattice RPAREN            { newExprFields(createUnaryOp(DontOpt, createParenthisizedLattice($3))) } 

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*=-=-=-=-=          Defining Left-Value Names            =-=-=-=-=-=*/
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
lvalue:
	  name                                        { $1 }
	| LPAREN lattice RPAREN                       { createParenthisizedLattice($2) }
	| CARET name                                  { newExprFields(createUnaryOp(DontOpt, $2)) }

name:
	  rest_of_name                                { newExprFields(NameListExpr($1)) }
	| at_operation                                { newExprFields(NameListExpr($1)) }
	| at_operation DOT rest_of_name               { newExprFields(NameListExpr(combineTwoNameLists($1, $3))) }

rest_of_name:
	  PREDEFINED_LABEL                            { {nameList = [PredefinedLabel(getPredefinedLabel($1))]} }
 	| label %prec NO_TOKEN                        { {nameList = [Id($1)]} }
	| rest_of_name DOT rest_of_name               { combineTwoNameLists($1, $3) }
	| rest_of_name LSQBRACKET altlat RSQBRACKET   { combineTwoNameLists($1, {nameList=[IndexingOp(Seq, $3)]}) } 
	| rest_of_name LBRACE altlat RBRACE           { combineTwoNameLists($1, {nameList=[IndexingOp(Alt, $3)] }) } 
	| LBRACE altlat RBRACE                        { {nameList = [Each($2)]} }

at_operation:
	  AT INTEGER                                  { createAtOperationOfInt($2) }
	| AT ID                                       { createAtOperationOfId($2) }
	| AT LPAREN altlat RPAREN                     { createAtOperationOfAltlat($3) }

number: 
	  INTEGER                                     { newExprFields(ConstExpr(IntLiteral($1))) }
	| FLOAT                                       { newExprFields(ConstExpr(FloatLiteral($1))) }
	
	
